/**
 * 
 */
package org.ccay.excel.importer.service.impl.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import org.ccay.core.log.ILogger;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.excel.exception.ExcelApplicationException;
import org.ccay.excel.importer.AbstractExcelDataProcessor;
import org.ccay.excel.importer.ExcelCellError;
import org.ccay.excel.importer.IExcelImportContext;
import org.ccay.excel.importer.ITitleMapper;
import org.ccay.excel.importer.RowData;
import org.ccay.excel.importer.validate.IStatus;
import org.ccay.excel.importer.validate.IValidationContext;
import org.ccay.excel.importer.validate.IValidator;
import org.ccay.excel.importer.validate.impl.ExcelValidatorManager;
import org.ccay.excel.meta.ColumnMeta;
import org.ccay.excel.meta.SheetMeta;
import org.ccay.excel.meta.ValidatorMeta;

/**
 * Excel数据校验器。
 * 
 * @author chaowangbang
 *
 */
public class ExcelValidator extends AbstractExcelDataProcessor {

	/** 日志记录器 **/
	private ILogger logger = CcayLoggerFactory.getLogger(ExcelValidator.class);

	/** 验证上下文 **/
	private ValidationContext validationContext = new ValidationContext();

	/** Expression的缓存，提高性能 **/
	private Map<String, Expression> expressionCache = new HashMap<String, Expression>();

	/** 估值上下文的缓存 **/
	private Map<String, EvaluationContext> evaludationContextCache = new HashMap<String, EvaluationContext>(); 

	private TemplateParserContext parserContext = new TemplateParserContext("{", "}");

	/** 表达式解析器 **/
	private ExpressionParser parser = new SpelExpressionParser();

	/* (non-Javadoc)
	 * @see org.ccay.excel.importer.service.impl.internal.IExcelDataProcessor#process(org.ccay.excel.importer.service.impl.internal.ExcelParseContext)
	 */
	public List<?> process(List<?> datas, IExcelImportContext context) throws ExcelApplicationException {
		if(datas==null || datas.isEmpty()) {
			return null;
		}

		validationContext.setSheetMeta(context.getCurrentSheet());
		validationContext.setResourceBundle(context.getResourceBundle());
		validationContext.setTitleMapper(context.getTitleMapper());

		List<RowData> validRowDatas = new ArrayList<RowData>(datas.size());
		for(int i=0; i<datas.size(); i++) {
			RowData rd = (RowData)datas.get(i);
			if(validateRowData(rd, context)) {
				validRowDatas.add(rd);
			}
		}

		return validRowDatas;
	}

	/** 验证行数据	
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @param rowData
	 * @throws ExcelApplicationException
	 */
	private boolean validateRowData(RowData rowData, IExcelImportContext context) throws ExcelApplicationException {
		// 设置行号和当前行的数据信息
		this.validationContext.setRowIndex(rowData.rowIndex);
		this.validationContext.setCurrentRowDatas(rowData);

		boolean isRowError = false;
		for(int i=0; i<rowData.colsIndex.length; i++) {
			String colIndex = rowData.colsIndex[i];
			Object data = rowData.colsData[i];
			if(!validateCellData(data, colIndex, context)) {
				isRowError = true;
			}
		}

		return !isRowError;
	}

	/** 验证单元格数据	
	 * @author chaowangbang
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @param data
	 * @param colIndex
	 * @return true, 单元格数据无错误; 否则为false
	 * 修改 2012-8-15
	 * @throws ExcelApplicationException
	 */
	private boolean validateCellData(Object data, String colIndex, IExcelImportContext context) throws ExcelApplicationException {
		// 设置列号
		this.validationContext.setColIndex(colIndex);

		String sheetName = context.getCurrentSheet().getPartSheetName();
		String excelSheetName = context.getNameDisplayNameMap().get(sheetName);
		List<IValidator> validators;
		
		if(context.getCurrentSheet().isChange()){
			validators=ExcelValidatorManager.getValidators(context.getCurrentSheet(), context.getTitleMapper().getName(colIndex));
		}else{
		// 获取该列对应的验证器列表
			validators = ExcelValidatorManager.getValidators(
					context.getExcelMeta(), // excelType
					context.getCurrentSheet().getSheetName(), 
					context.getTitleMapper().getName(colIndex)
					);
		}

		boolean isError = false;
		for(Iterator<IValidator> iter = validators.iterator(); iter.hasNext(); ) {
			IValidator validator = iter.next();
			// 设置验证器上下文
			validator.setValidationContext(this.validationContext);
			// 设置Excel导入时的上下文
			validator.setExcelImportContext(context);
			// 数据校验
			IStatus status = validator.validate(data);
			if(!status.isOK()) {
				isError = true;

				ExcelCellError error = new ExcelCellError(
						excelSheetName, // sheet的名称
						this.validationContext.getRowIndex(), // 行索引 
						colIndex, // 列索引
						status.getMessage()); // 消息
				//change by zkf69290  添加错误信息
				context.addCellError(error);  //添加错误信息
				
				if(validator.isShortCircuit()) {
					break;
				}
			}
		}

		return !isError;
	}

	class ValidationContext implements IValidationContext {

		/** 对应的Sheet **/
		private SheetMeta sheetMeta;

		/** 当前行的数据 **/
		private RowData currentRowDatas;

		/** 当前正在校验的数据行 **/
		private int currentRow;

		/** 当前正在校验的列索引号 **/
		private String colIndex;

		private ResourceBundle resourceBundle;

		private ITitleMapper titleMapper;

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getCurrentRowDatas()
		 */
		public RowData getCurrentRowDatas() {
			return this.currentRowDatas;
		}

		/** 设置当前校验行的数据。
		 * 数据格式为：列号:数据值	
		 * @author chaowangbang
		 * @since Mar 22, 2012
		 * @param rowDatas
		 */
		public void setCurrentRowDatas(RowData rowDatas) {
			this.currentRowDatas = rowDatas;
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getSheetMeta()
		 */
		public SheetMeta getSheetMeta() {
			return this.sheetMeta;
		}

		/** 设置当前校验的Sheet	
		 * @author chaowangbang
		 * @since Mar 22, 2012
		 * @param sheetMeta
		 */
		public void setSheetMeta(SheetMeta sheetMeta) {
			this.sheetMeta = sheetMeta;
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getColIndex()
		 */
		public String getColIndex() {
			return this.colIndex;
		}

		/** 设置当前校验的列	
		 * @author chaowangbang
		 * @since Mar 22, 2012
		 * @param colIndex
		 */
		public void setColIndex(String colIndex) {
			this.colIndex = colIndex;
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getRowIndex()
		 */
		public int getRowIndex() {
			return this.currentRow;
		}

		/** 设置当前校验的行	
		 * @author chaowangbang
		 * @since Mar 22, 2012
		 * @param rowIndex
		 */
		public void setRowIndex(int rowIndex) {
			this.currentRow = rowIndex;
		}

		/**
		 * @param resourceBundle
		 */
		public void setResourceBundle(ResourceBundle resourceBundle) {
			this.resourceBundle = resourceBundle;
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getText(java.lang.String)
		 */
		public String getText(String messageKey, String defaultMessage, IValidator validator) {
			String messageTemplate = getMessageTemplate(messageKey, defaultMessage);
			if(messageTemplate == null) {
				return "";
			}

			EvaluationContext context = getOrCreateEvaluationContext(validator);
			return evaluate(messageTemplate, context);
		}

		/**
		 * 消息估值。
		 * 
		 * @param messageTemplate
		 * @param context
		 * @return
		 */
		private String evaluate(String messageTemplate, EvaluationContext context) {
			//只允许变量引用,避免安全漏洞 gwx409044 2016-11-01
			messageTemplate = messageTemplate.replaceAll("\\{[^#]", "");
			messageTemplate = messageTemplate.replaceAll("\\{#[^\\}]*\\.[^\\}]*\\}", "");
			if(!expressionCache.containsKey(messageTemplate)) {
				Expression exp = parser.parseExpression(messageTemplate, parserContext);
				expressionCache.put(messageTemplate, exp);
			}

			Expression expression = expressionCache.get(messageTemplate);
			try {
				return expression.getValue(context, String.class);
			} catch (EvaluationException e) {
				return "@@@Internal error: "+e.getMessage();
			}
		}

		/**
		 * 获取消息模板
		 * 
		 * @param messageKey
		 * @param defaultMessage
		 * @return
		 */
		private String getMessageTemplate(String messageKey, String defaultMessage) {
			String messageTemplate = null;
			try {
				messageTemplate = this.resourceBundle.getString(messageKey);
			} catch (MissingResourceException e) {
				logger.warn("I18N resource key '"+messageKey+"' has not be defined.");
			}

			if(messageTemplate == null) {
				messageTemplate = defaultMessage;
			}

			return messageTemplate;
		}

		/**
		 * 创建或获取估值上下文。
		 * 
		 * @param validatorType
		 * @return
		 */
		private EvaluationContext getOrCreateEvaluationContext(IValidator validator) {
			SheetMeta sheetMeta = getSheetMeta();
			ColumnMeta columnMeta = getColumnMeta();

			StringBuffer buf = new StringBuffer(sheetMeta.getName());
			buf.append(':');
			buf.append(columnMeta.getName());
			buf.append(':');
			buf.append(validator.getType());

			String key = buf.toString();
			if(evaludationContextCache.containsKey(key)) {
				return evaludationContextCache.get(key);
			}

			EvaluationContext context = new StandardEvaluationContext(validator);

			context.setVariable("sheetName", sheetMeta.getName());
			context.setVariable("columnName", columnMeta.getDisplayName());
			//context.setVariable("value", this.value);

			ValidatorMeta validatorMeta = columnMeta.getValidatorMeta(validator.getType());
			if(validatorMeta != null) {
				Map<String, String> params = validatorMeta.getParams();
				Iterator<Entry<String, String>> iter = params.entrySet().iterator();
				while(iter.hasNext()) {
					Entry<String, String> param = iter.next();
					context.setVariable(param.getKey(), param.getValue());
				}
			}

			evaludationContextCache.put(key, context);

			return context;
		}

		/**
		 * 上下文复位
		 */
		public void reset() {
			this.sheetMeta = null;
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getColumnMeta()
		 */
		public ColumnMeta getColumnMeta() {
			String name = this.titleMapper.getName(this.colIndex);
			return sheetMeta.getColumnByName(name);
		}

		/* (non-Javadoc)
		 * @see org.ccay.excel.importer.validate.IValidationContext#getTitleMapper()
		 */
		public ITitleMapper getTitleMapper() {
			return this.titleMapper;
		}

		/**
		 * 设置标题列映射信息。
		 * 
		 * @param titleMapper
		 */
		public void setTitleMapper(ITitleMapper titleMapper) {
			this.titleMapper = titleMapper;
		}

	}

}
